/*
 *  boot/head.S
 *
 *  last modified 2007.07.14 21:17 By ChenJie
 *	
 *  主要功能描述:
 *	1.加载内核真正的gdt, idt, 配置ds, es, fs, gs, ss.
 *	2.检查A20地址线是否打开, 若未打开则进入死循环.
 *	3.检查是否有协处理器, 有则set MP=1, 否则set EM=1.
 *	4.跳转执行main函数.
 *
 *  主要数据描述:
 *	1. pg_dir, 页目录表, 	   4KB, 0x00000000~0x00000FFF
 *	2. init_task, Task0进程块, 4KB, 0x00001000~0x00001FFF
 *	3. idt, 中断向量描述符表,  2KB, 0x00002000~0x000027FF
 *	4. gdt, 全局描述符表,      2KB, 0x00002800~0x00002FFF
 *	5. user_stack, Task0用户态堆栈, 大小不确定, 末端地址是0xA0000
 */	

#include <clinux/config.h>
#include <asm/asmdef.h>
#include <asm/i386.h>

.text
.extern main
ENTRY(pg_dir)
startup_32:
	movl $0x10,%eax
	mov %ax,%ds
	
	lgdt gdt_ptr			# 加载gdt
	lidt idt_ptr			# 加载idt
	lss  kernel_stack_ptr, %esp	# 暂时使用Task0的用户态堆栈
	
	movw $SELECTOR_KDS, %ax		# 使用内核数据段
	movw %ax, %ds
	movw %ax, %es
	movw %ax, %fs
	movw $SELECTOR_KGS, %ax
	movw %ax, %gs

check_A20:				# Check that A20 really is enabled
	xorl %eax, %eax
1:					# loop forever if it isn't
	incl %eax
	movl %eax, 0x000000
	cmpl %eax, 0x100000
	je 1b

check_math_chip:
	movl %cr0, %eax
	andl $0x80000011, %eax		# save PG, PE, ET
					# PG=0x10000000 是否启用分页
					# ET=0x00000010 扩展类型，指出协处理器
					#	类型是80287还是80387
					# PE=0x00000001 是否保护模式

	orl  $0x00000002, %eax		# set MP=1, 假设协处理器存在
	movl %eax, %cr0
	call check_x87
	
	jmp  setup_paging

check_x87:				# 检查是否存在x87, 并修改cr0相应位
	fninit
	fstsw %ax
	cmpb $0, %al
	je 1f				# 如果存在x87, 则跳转到标号1处
	movl %cr0, %eax
	xorl $0x00000006, %eax		# set MP=0, set EM=1 协处理器仿真位
	movl %eax, %cr0
	ret
.align 4
1:	.byte 0xDB, 0xE4		# 287协处理器码 fsetpm, 387忽略此指令
	ret

#################################################################
.align 4			# 4字节对齐
	.word 0
idt_ptr:			# idt加载指针
	.word NR_IDT_ENTRY*8-1
	.long idt

################################################################
.align 4			# 4字节对齐
	.word 0
gdt_ptr:			# gdt加载指针
	.word NR_GDT_ENTRY*8-1
	.long gdt

################################################################
.align 4			# 4字节对齐
kernel_stack_ptr:			# task0用户态堆栈加载指针
	.long TASK0_ESP
	.word SELECTOR_KDS

###############################################################

.org 0x1000
.align 8
ENTRY(init_proc)		# 用于Task0的进程控制块和内核态堆栈
setup_paging:

	jmp main
	hlt

.org 0x2000
.align 16
ENTRY(idt)
	.fill NR_IDT_ENTRY, 8, 0

.align 16
ENTRY(gdt)
	Descriptor(0x00000000, 0x00000, 0x0)
	Descriptor(0x00000000, 0xfffff, DA_CR |DA_32|DA_DPL0|DA_LIMIT_4K)
	Descriptor(0x00000000, 0xfffff, DA_DRW|DA_32|DA_DPL0|DA_LIMIT_4K)
	Descriptor(0x000b8000, 0x0ffff, DA_DRW|DA_32|DA_DPL3|DA_LIMIT_1B)
	.fill NR_GDT_ENTRY-4, 8, 0
